// synch.cc 
//	Routines for synchronizing threads.  Three kinds of
//	synchronization routines are defined here: semaphores, locks 
//   	and condition variables (the implementation of the last two
//	are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	Initialize a semaphore, so that it can be used for synchronization.
//
//	"debugName" is an arbitrary name, useful for debugging.
//	"initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore(char* debugName, int initialValue)
{
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	De-allocate semaphore, when no longer needed.  Assume no one
//	is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore()
{
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
// 	Wait until semaphore value > 0, then decrement.  Checking the
//	value and decrementing must be done atomically, so we
//	need to disable interrupts before checking the value.
//
//	Note that Thread::Sleep assumes that interrupts are disabled
//	when it is called.
//----------------------------------------------------------------------

void
Semaphore::P()
{
    IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
    
    while (value == 0) { 			// semaphore not available
	queue->Append((void *)currentThread);	// so go to sleep
	currentThread->Sleep();
    } 
    value--; 					// semaphore available, 
						// consume its value
    
    (void) interrupt->SetLevel(oldLevel);	// re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
// 	Increment semaphore value, waking up a waiter if necessary.
//	As with P(), this operation must be atomic, so we need to disable
//	interrupts.  Scheduler::ReadyToRun() assumes that threads
//	are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);

    thread = (Thread *)queue->Remove();
    if (thread != NULL)	   // make thread ready, consuming the V immediately
	scheduler->ReadyToRun(thread);
    value++;
    (void) interrupt->SetLevel(oldLevel);
}
#ifdef CHANGED
// Dummy functions -- so we can compile our later assignments 
// Note -- without a correct implementation of Condition::Wait(), 
// the test case in the network assignment won't work!
Lock::Lock(char* debugName) {
  name = debugName;
  queue = new List;
  owner = NULL; //Lock is FREE
}

Lock::~Lock() {
  delete queue;
}

void Lock::Acquire() {
  IntStatus oldLevel = interrupt->SetLevel(IntOff);  //disable interrupts
  if(owner == currentThread){                 //lock owned by current thread
	printf("***ERROR in Acquiring Lock %s\n", name);
    (void) interrupt->SetLevel(oldLevel); //restore interrupts
	return;
  }
  
  if(owner == NULL){                    //lock available
    owner = currentThread;
    (void) interrupt->SetLevel(oldLevel); //restore interrupts
  }
  else{    
    queue->Append((void *)currentThread);  //lock is already in use; now waiting
    currentThread->Sleep();
  }
  (void) interrupt->SetLevel(oldLevel); //restore interrupts
}

void Lock::Release() {
  IntStatus oldLevel = interrupt->SetLevel(IntOff);  //disable interrupts
  if(owner != currentThread){                       //if thread isn't the owner
	printf("***ERROR in Releasing Lock %s\n", name);
    (void) interrupt->SetLevel(oldLevel); //restore interrupts
	return;
  }
  
  owner = (Thread *)queue->Remove();
  if(owner != NULL){                        //if there is a thread waiting
    scheduler->ReadyToRun(owner); //put thread in ready queue in ready state
    (void) interrupt->SetLevel(oldLevel); //restore interrupts
	return;
  }
  else{                                           //if no thread waiting
    owner = NULL; //lock state is FREE
  }
  (void) interrupt->SetLevel(oldLevel); //restore interrupts
}

bool Lock::isHeldByCurrentThread(){
	return owner == currentThread;
}

/* Begin Condition */
Condition::Condition(char* debugName) { 
  waitingLock = NULL;
  name = debugName;
  waitQueue = new List;
  thread = NULL;
}

Condition::~Condition() { 
  delete waitQueue;
}

int Condition::isEmpty(){
	if(waitQueue->IsEmpty()){
		return 1;
	} else {
		return 0;
	}
}

void Condition::Wait(Lock* conditionLock) { 
  IntStatus oldLevel = interrupt->SetLevel(IntOff);  //disable interrupts
  
  if(conditionLock == NULL){
    printf("***ERROR in Condition Waiting %s\n", name);
    (void) interrupt->SetLevel(oldLevel); //restore interrupts
	return;
  }

  if(waitingLock == NULL){ 	//first thread calling wait on this condition variable
	waitingLock = conditionLock;
  }
  
  if(waitingLock != conditionLock){
	printf("***ERROR in Condition Waiting! waitingLock != conditionLock: %s\n", name);
	(void) interrupt->SetLevel(oldLevel); //restore interrupts
	return;
  }
  
  // ok to wait
  conditionLock->Release(); //exit monitor
  //printf("CATTLE BRUISER OPERATIONAL\n");
  waitQueue->Append((void *)currentThread);  //lock is already in use; now waiting
  //printf("BATTLE CRUISER OPERATIONAL\n");
  /* processTable[currentThread->spaceId]->processTableEntryLock->Acquire();
  processTable[currentThread->spaceId]->numAliveThreads--; // decrement number of alive threads
  processTable[currentThread->spaceId]->processTableEntryLock->Release(); */
  currentThread->Sleep();
  
  conditionLock->Acquire();
  (void) interrupt->SetLevel(oldLevel); //restore interrupts
}
void Condition::Signal(Lock* conditionLock) { 
  IntStatus oldLevel = interrupt->SetLevel(IntOff);  //disable interrupts
  
  if(waitQueue->IsEmpty()){ //nothing in wait queue
    //printf("***Condition Signal! Nothing in wait queue: %s\n", name);
	(void) interrupt->SetLevel(oldLevel); //restore interrupts
	return;
  }

  if(waitingLock != conditionLock){
	printf("***ERROR in Condition Signal! waitingLock != conditionLock: %s\n", name);
	(void) interrupt->SetLevel(oldLevel); //restore interrupts
	return;
  }

  // wake up one from waitQueue, thread should not be null at this point
  thread = (Thread *)waitQueue->Remove();
  /* processTable[currentThread->spaceId]->processTableEntryLock->Acquire();
  processTable[currentThread->spaceId]->numAliveThreads++; // increment number of alive threads
  processTable[currentThread->spaceId]->processTableEntryLock->Release(); */
  scheduler->ReadyToRun(thread); //put thread in ready queue in ready state
  
  if(waitQueue->IsEmpty()){ //no more waiting threads
	waitingLock = NULL;
  }
  
}
void Condition::Broadcast(Lock* conditionLock) {
  while(!waitQueue->IsEmpty()){ //there are threads waiting
	Condition::Signal(conditionLock);
  }
}
#endif